﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MicroBenchmarks.InternalClass
{
    public interface ICount
    {
        int GetCount();
        void IncrCount();
        void IncrCount1();
        void DecrCount();
        int Count { get; }
    }

    public class CountCls
        : ICount
    {
        int count;
        
        public int Count
        {
            get
            {
                return this.count;
            }
        }

        #region ICount Members
        public int GetCount()
        {
            return count;
        }

        public void IncrCount()
        {
            count++;
        }

        public void IncrCount1()
        {
            count++;
        }

        public void DecrCount()
        {
            count--;
        }
        #endregion
    }

    public class InnerStack
    {
        ICount countobj;
        int[] elements;
        int currentPosition;

        public InnerStack()
        {
            elements = new int[20];
            currentPosition = 0;            
        }

        public void InitializeCountCls()
        {
            countobj = new CountCls();
        }

        public void Push(int i)
        {
            if (currentPosition >= 20)
                throw new Exception("Stack full");
            elements[currentPosition++] = i;
            if(countobj != null)
                countobj.IncrCount();
        }

        public void Push1(int i)
        {
            if (currentPosition >= 20)
                throw new Exception("Stack full");
            elements[currentPosition++] = i;
            if (countobj != null)
                countobj.IncrCount1();
        }

        public int Pop()
        {
            if (currentPosition == 0)
                throw new Exception("Stack empty");

            if (countobj != null)
                countobj.DecrCount();

            return elements[--currentPosition];
        }

        public int Size()
        {
            if (countobj != null)
                return countobj.Count;
            else
                return 0;
        }

        public void Clear()
        {
            elements = null;
            countobj = null;
        }

        public void reinitialize()
        {
            elements = new int[20];
            currentPosition = 0;
            countobj = new CountCls();
        }
    }

    /// <summary>
    /// A variant of inner stack that throws exception as this requires
    /// another pre-condition
    /// </summary>
    public class InnerStackVariant
    {
        ICount countobj;
        int[] elements;
        int currentPosition;

        public InnerStackVariant()
        {
            elements = new int[20];
            currentPosition = 0;
        }

        int checkpoint = 0;
        public void DependentCountCls()
        {
            checkpoint = 1;
        }

        public void InitializeCountCls()
        {
            if (checkpoint == 0)
                throw new Exception("checkpoint not met");
            countobj = new CountCls();
        }

        public void Push(int i)
        {
            if (currentPosition >= 20)
                throw new Exception("Stack full");
            elements[currentPosition++] = i;
            if (countobj != null)
                countobj.IncrCount();
        }

        public void Push1(int i)
        {
            if (currentPosition >= 20)
                throw new Exception("Stack full");
            elements[currentPosition++] = i;
            if (countobj != null)
                countobj.IncrCount1();
        }

        public int Pop()
        {
            if (currentPosition == 0)
                throw new Exception("Stack empty");

            if (countobj != null)
                countobj.DecrCount();

            return elements[--currentPosition];
        }

        public int Size()
        {
            if (countobj != null)
                return countobj.Count;
            else
                return 0;
        }

        public void Clear()
        {
            elements = null;
            countobj = null;
        }

        public void reinitialize()
        {
            elements = new int[20];
            currentPosition = 0;            
        }
    }

    /// <summary>
    /// A special category where the object state is required by deeper class
    /// but none of its methods are exposed outside. A common scenario of companies 
    /// code bases where much of the functionality is hidden in the internal classes
    /// SIDE AFFECT FREE Class
    /// </summary>
    public class OuterStack
    {       
        private InnerStack istack;
        public OuterStack()
        {
            istack = new InnerStack();
        }

        public void Push(int i)
        {
            istack.Push(i);
        }

        public void Push1(int i)
        {
            istack.Push1(i);
        }

        public int Pop()
        {
            return istack.Pop();
        }

        public int Size()
        {
            return istack.Size();
        }

        public void InitializeStackSecond()
        {
            istack.InitializeCountCls();
        }

        public void ClearStack()
        {
            istack.Clear();
        }
    }

    /// <summary>
    /// A variant of the outer stack
    /// </summary>
    public class OuterStackVariant
    {
        private InnerStackVariant istack;
        public OuterStackVariant()
        {
            istack = new InnerStackVariant();
        }

        public void Push(int i)
        {
            istack.Push(i);
        }

        public void Push1(int i)
        {
            istack.Push1(i);
        }

        public int Pop()
        {
            return istack.Pop();
        }

        public int Size()
        {
            return istack.Size();
        }

        public void InitializeStackSecond()
        {
            istack.InitializeCountCls();
        }

        public void ClearStack()
        {
            istack.Clear();
        }
    }
}
